package com.br.cashflowdao.hibernate.dao;

import com.br.cashflowdao.hibernate.dao.util.HibernateUtil;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;

/**
 * Abstract class with default implementation of all methods defined in the
 * interface <code>IDAO</code>.
 * <p>
 * Any DAO seeking for <b>default</b> CRUD operation can extends this class in order
 * to use the functionalities available through this class.
 * <p>
 *
 * @param <code><T></code> entity used for CRUD purpose
 * @param <code><ID></code> entity's identifier
 *
 * @author Marcel Sena <marcel.sena at marcel.sena@gmail.com>
 * @version 1.0
 * @since 05/01/2009
 */
 abstract class AbstractDAO<T, ID extends Serializable>  implements IDAO<T, ID>, Serializable {

   private Class<T> persistentClass;
   private Session session;
   private Transaction tx;

    /**
     * Default constructor
     */
    public AbstractDAO() {
        this.persistentClass =
                (Class<T>) (
                             (ParameterizedType) getClass().getGenericSuperclass()
                            )
                              .getActualTypeArguments()[0];
     }

    /**
     *@see <code>com.br.cashflowdao.hibernate.dao.IDAO.findById(ID id)</code>
     */
    @Override
    public T findById(ID id) {
       T entity = null;
       try {
            session = HibernateUtil.getSessionFactory().openSession();
            entity = (T) session.get(persistentClass, id);
        } catch (Exception ex) {
            Logger.getLogger(AbstractDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            session.close();
        }
        return entity;
  }

    /**
     * @see <code>com.br.cashflowdao.hibernate.dao.IDAO.findAll()</code>
     * @see #findByCriteria(Criterion)
     */
    @Override
    public List<T> findAll() {
        return findByCriteria();
    }

    /**
     * @see <code>com.br.cashflowdao.hibernate.dao.IDAO.findByExample()(T entityExample, String[] excludeProperty)</code>
     */
    @Override
    public List<T> findByExample(T entityExample, String[] excludeProperty) {
        Criteria crit = null;
        List result = null;
        try {
            session = HibernateUtil.getSessionFactory().openSession();
            crit = session.createCriteria(persistentClass);
            Example example =  Example.create(entityExample);
            for (String exclude : excludeProperty) {
                example.excludeProperty(exclude);
            }
            crit.add(example);
            result = crit.list();

         } catch (Exception ex) {
            Logger.getLogger(AbstractDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            session.close();
        }
       return result;
    }

    /**
     * @see <code>com.br.cashflowdao.hibernate.dao.IDAO.makePersistent(T entity)</code>
     */
    @Override
    public T createOrUpdate(T entity) {
        try {
            session = HibernateUtil.getSessionFactory().openSession();
            tx = session.beginTransaction();
            session.saveOrUpdate(entity);
            tx.commit();
        } catch (Exception ex) {
            tx.rollback();
            Logger.getLogger(AbstractDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            //session.close();
        }
            return entity;
    }

    /**
     * @see<code> com.br.cashflowdao.hibernate.dao.IDAO.makeTransient(T entity)</code>
     */
    @Override
    public void remove(T entity) {
        try {
            session = HibernateUtil.getSessionFactory().openSession();
            tx = session.beginTransaction();
            session.delete(entity);
            tx.commit();
        } catch (Exception ex) {
           tx.rollback();
           Logger.getLogger(AbstractDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            session.close();
        }
    }

    /**
     * This method uses the a <code>Criterion</code> for querying purpose.
     * Use this inside subclasses as a convenience method.
     * <p>
     * 
     * @param criterion
     * @return a list containing all the entities found or <code>null</code>
     * if no data is found.
     */
    public List<T> findByCriteria(Criterion... criterion) {
      Criteria crit = null;
      List result = null;
      try {
            session = HibernateUtil.getSessionFactory().openSession();
            crit = session.createCriteria(persistentClass);
            for (Criterion c : criterion) {
                crit.add(c);
            }
            result = crit.list();
        } catch (Exception ex) {
            Logger.getLogger(AbstractDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            session.close();
        }
        return result;
   }

}